#include "LogicSystem.h"
#include "CSession.h"
#include "ConfigManager.h"
#include "MysqlManager.h"
#include "RedisManager.h"
#include "UserManager.h"
#include "ChatGrpcClient.h"

#include <json/reader.h>
#include <json/json.h>
#include <iostream>

LogicSystem::LogicSystem() : m_b_stop(false) 
{
	registerCallbacks();
	m_worker_thread = std::thread(&LogicSystem::dealMsg, this);
}

// 判断是否全是数字
bool LogicSystem::isPureDigit(const std::string& str)
{
	for (const auto& c : str) {
		if (!std::isdigit(c)) {
			return false;
		}
	}
	return true;
}

void LogicSystem::getUserByUid(std::string uid_str, Json::Value& rtvalue)
{
	rtvalue["error"] = ErrorCodes::Success;
	std::string base_key = USER_BASE_INFO + uid_str;

	// 优先查redis中查询用户信息
	std::string info_str = "";
	bool b_base = RedisManager::getInstance()->get(base_key, info_str);
	if (b_base) {
		Json::Reader reader;
		Json::Value root;
		reader.parse(info_str, root);
		auto uid = root["uid"].asInt();
		auto name = root["name"].asString();
		auto pwd = root["pwd"].asString();
		auto email = root["email"].asString();
		auto nick = root["nick"].asString();
		auto desc = root["desc"].asString();
		auto sex = root["sex"].asInt();
		auto icon = root["icon"].asString();
		std::cout << "user  uid is  " << uid << " name  is " << name << " pwd is " << pwd 
				  << " email is " << email << " icon is " << icon << std::endl;

		rtvalue["uid"] = uid;
		rtvalue["pwd"] = pwd;
		rtvalue["name"] = name;
		rtvalue["email"] = email;
		rtvalue["nick"] = nick;
		rtvalue["desc"] = desc;
		rtvalue["sex"] = sex;
		rtvalue["icon"] = icon;
		return;
	}

	auto uid = std::stoi(uid_str);
	// redis中没有查询到用户信息，则查询mysql数据库
	std::shared_ptr<UserInfo> user_info = nullptr;
	user_info = MysqlManager::getInstance()->getUserInfo(uid);
	if (user_info == nullptr) {
		rtvalue["error"] = ErrorCodes::UidInvalid;
		return;
	}

	// 将数据库内容写入redis缓存
	Json::Value redis_root;
	redis_root["uid"] = user_info->uid;
	redis_root["pwd"] = user_info->pwd;
	redis_root["name"] = user_info->name;
	redis_root["email"] = user_info->email;
	redis_root["nick"] = user_info->nick;
	redis_root["desc"] = user_info->desc;
	redis_root["sex"] = user_info->sex;
	redis_root["icon"] = user_info->icon;

	RedisManager::getInstance()->set(base_key, redis_root.toStyledString());

	// 返回数据
	rtvalue["uid"] = user_info->uid;
	rtvalue["pwd"] = user_info->pwd;
	rtvalue["name"] = user_info->name;
	rtvalue["email"] = user_info->email;
	rtvalue["nick"] = user_info->nick;
	rtvalue["desc"] = user_info->desc;
	rtvalue["sex"] = user_info->sex;
	rtvalue["icon"] = user_info->icon;
}

void LogicSystem::getUserByName(std::string name, Json::Value& rtvalue)
{
	rtvalue["error"] = ErrorCodes::Success;

	std::string base_key = NAME_INFO + name;

	// 先查redis中查询用户信息
	std::string info_str = "";
	bool b_base = RedisManager::getInstance()->get(base_key, info_str);
	if (b_base) {
		Json::Reader reader;
		Json::Value root;
		reader.parse(info_str, root);
		auto uid = root["uid"].asInt();
		auto name = root["name"].asString();
		auto pwd = root["pwd"].asString();
		auto email = root["email"].asString();
		auto nick = root["nick"].asString();
		auto desc = root["desc"].asString();
		auto sex = root["sex"].asInt();
		std::cout << "user  uid is  " << uid << " name  is " << name << " pwd is " << pwd 
				  << " email is " << email << std::endl;

		rtvalue["uid"] = uid;
		rtvalue["pwd"] = pwd;
		rtvalue["name"] = name;
		rtvalue["email"] = email;
		rtvalue["nick"] = nick;
		rtvalue["desc"] = desc;
		rtvalue["sex"] = sex;
		return;
	}

	//redis中没有则查询mysql
	//查询数据库
	std::shared_ptr<UserInfo> user_info = nullptr;
	user_info = MysqlManager::getInstance()->getUserInfo(name);
	if (user_info == nullptr) {
		rtvalue["error"] = ErrorCodes::UidInvalid;
		return;
	}

	//将数据库内容写入redis缓存
	Json::Value redis_root;
	redis_root["uid"] = user_info->uid;
	redis_root["pwd"] = user_info->pwd;
	redis_root["name"] = user_info->name;
	redis_root["email"] = user_info->email;
	redis_root["nick"] = user_info->nick;
	redis_root["desc"] = user_info->desc;
	redis_root["sex"] = user_info->sex;

	RedisManager::getInstance()->set(base_key, redis_root.toStyledString());

	// 返回数据
	rtvalue["uid"] = user_info->uid;
	rtvalue["pwd"] = user_info->pwd;
	rtvalue["name"] = user_info->name;
	rtvalue["email"] = user_info->email;
	rtvalue["nick"] = user_info->nick;
	rtvalue["desc"] = user_info->desc;
	rtvalue["sex"] = user_info->sex;
}

// 从mysql获取好友申请列表
bool LogicSystem::getFriendApplyInfo(int to_uid, std::vector<std::shared_ptr<ApplyInfo>>& list)
{
	return MysqlManager::getInstance()->getApplyList(to_uid, list, 0, 10);
}

bool LogicSystem::getFriendList(int self_id, std::vector<std::shared_ptr<UserInfo>>& user_list)
{
	return MysqlManager::getInstance()->getFriendList(self_id, user_list);
}

// 从Redis中查询用户信息, 如果为查询到，则查询Mysql，并将数据写入Redis
bool LogicSystem::getBaseInfo(std::string base_key, int uid, std::shared_ptr<UserInfo>& userinfo)
{
	// 先查redis中查询用户信息
	std::string info_str = "";
	bool res = RedisManager::getInstance()->get(base_key, info_str);
	if (res) {
		Json::Reader reader;
		Json::Value root;
		reader.parse(info_str, root);
		userinfo->uid = root["uid"].asInt();
		userinfo->name = root["name"].asString();
		userinfo->pwd = root["pwd"].asString();
		userinfo->email = root["email"].asString();
		userinfo->nick = root["nick"].asString();
		userinfo->desc = root["desc"].asString();
		userinfo->sex = root["sex"].asInt();
		userinfo->icon = root["icon"].asString();
		std::cout << "user login uid is  " << userinfo->uid << " name  is " << userinfo->name
			<< " pwd is " << userinfo->pwd << " email is " << userinfo->email
			<< std::endl;
	}
	else {
		// redis中没有, 则查询mysql
		std::shared_ptr<UserInfo> user_info = nullptr;
		user_info = MysqlManager::getInstance()->getUserInfo(uid);
		if (user_info == nullptr) {
			return false;
		}

		// 将Mysql数据库内容写入redis缓存
		Json::Value redis_root;
		redis_root["uid"] = uid;
		redis_root["pwd"] = userinfo->pwd;
		redis_root["name"] = userinfo->name;
		redis_root["email"] = userinfo->email;
		redis_root["nick"] = userinfo->nick;
		redis_root["desc"] = userinfo->desc;
		redis_root["sex"] = userinfo->sex;
		redis_root["icon"] = userinfo->icon;
		RedisManager::getInstance()->set(base_key, redis_root.toStyledString());
	}

	return true;
}

void LogicSystem::dealMsg()
{
	for (;;)
	{
		std::unique_lock<std::mutex> lock(m_mtex);
		// 判断任务队列为空并且未处于关闭状态，则用条件遍历进行阻塞等待
		while (m_msg_que.empty() && !m_b_stop) {
			m_cond.wait(lock);
		}

		// 判断是否关闭状态，如果为关闭状态，则执行完所有逻辑之后就退出循环
		if (m_b_stop) {
			// 清空队列
			while (!m_msg_que.empty()) {
				processMessage(m_msg_que.front());
			}
			break;
		}
		
		// 如果未处于关闭状态
		if (!m_msg_que.empty()) {
			processMessage(m_msg_que.front());
		}
	}
}

void LogicSystem::processMessage(std::shared_ptr<LogicNode> msg_node)
{
	std::cout << "recv_msg id is " << msg_node->m_recvnode->m_msg_id << std::endl;
	auto it_call_back = m_fun_callbacks_map.find(msg_node->m_recvnode->m_msg_id);
	if (it_call_back != m_fun_callbacks_map.end()) {
		// 进行回调
		it_call_back->second(msg_node->m_session, msg_node->m_recvnode->m_msg_id,
			std::string(msg_node->m_recvnode->m_data, msg_node->m_recvnode->m_cur_len)
		);
	}
	else {
		std::cout << "msg id [" << msg_node->m_recvnode->m_msg_id << "] handler not found" << std::endl;
	}

	// 处理完消息后从队列中移除
	m_msg_que.pop();
}


LogicSystem::~LogicSystem()
{
	m_b_stop = true;
	m_cond.notify_one();
	m_worker_thread.join();
}

void LogicSystem::postMsgToQue(std::shared_ptr<LogicNode> msg)
{
	std::unique_lock<std::mutex> lock(m_mtex);
	m_msg_que.push(msg);
	// 防止队列过大
	if (m_msg_que.size() > MAX_SENDQUE) {
		std::cout << "session: " << msg->m_session->getSessionId() << " send que fulled, size is " << MAX_SENDQUE << std::endl;
		return;
	}

	// 任务队列由0变为1，则发送通知信号
	if (m_msg_que.size() == 1) {
		lock.unlock();
		m_cond.notify_one();
	}
}

void LogicSystem::registerCallbacks()
{
	m_fun_callbacks_map[MSG_CHAT_LOGIN] = std::bind(&LogicSystem::loginHandler, this, 
		std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);

	m_fun_callbacks_map[ID_SEARCH_USER_REQ] = std::bind(&LogicSystem::searchInfo, this,
		std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);

	m_fun_callbacks_map[ID_ADD_FRIEND_REQ] = std::bind(&LogicSystem::addFriendApply, this,
		std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);

	m_fun_callbacks_map[ID_AUTH_FRIEND_REQ] = std::bind(&LogicSystem::authFriendApply, this,
		std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);

	m_fun_callbacks_map[ID_TEXT_CHAT_MSG_REQ] = std::bind(&LogicSystem::dealChatTextMsg, this,
		std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
}

// 进行登录消息的回调 TODO：可以优化功能
void LogicSystem::loginHandler(std::shared_ptr<CSession> session, const short& msg_id, const std::string& msg_data)
{
	// 解析登录的json消息
	Json::Reader reader;
	Json::Value root;
	reader.parse(msg_data, root);
	auto uid = root["uid"].asInt();
	auto token = root["token"].asString();
	std::cout << "user login is " << uid << std::endl;
	std::cout << "user token is " << token << std::endl;

	Json::Value ret_value;
	Defer defer([this, &ret_value, session]() {
		std::string return_str = ret_value.toStyledString();
		// 给客户端的登录请求回包
		session->Send(return_str, MSG_CHAT_LOGIN_RSP);
	});

	// 从redis获取用户token是否正确
	std::string uid_str = std::to_string(uid);
	std::string token_key = USERTOKENPREFIX + uid_str;
	std::string token_value = "";
	bool res = RedisManager::getInstance()->get(token_key, token_value);
	if (!res) {
		ret_value["error"] = ErrorCodes::UidInvalid;
		return;
	}

	if (token_value != token) {
		ret_value["error"] = ErrorCodes::TokenInvalid;
		return;
	}


	ret_value["error"] = ErrorCodes::Success;
	
	
	std::string base_key = USER_BASE_INFO + uid_str;
	auto user_info = std::make_shared<UserInfo>();
	bool b_base = getBaseInfo(base_key, uid, user_info);
	if (!b_base) {
		ret_value["error"] = ErrorCodes::UidInvalid;
		return;
	}
	ret_value["uid"] = uid;
	ret_value["pwd"] = user_info->pwd;
	ret_value["name"] = user_info->name;
	ret_value["email"] = user_info->email;
	ret_value["nick"] = user_info->nick;
	ret_value["desc"] = user_info->desc;
	ret_value["sex"] = user_info->sex;
	ret_value["icon"] = user_info->icon;

	// 从数据库获取申请列表
	std::vector<std::shared_ptr<ApplyInfo>> apply_list;
	auto b_apply = getFriendApplyInfo(uid, apply_list);
	if (b_apply) {
		for (auto& apply : apply_list) {
			Json::Value obj;
			obj["name"] = apply->m_name;
			obj["uid"] = apply->m_uid;
			obj["icon"] = apply->m_icon;
			obj["nick"] = apply->m_nick;
			obj["sex"] = apply->m_sex;
			obj["desc"] = apply->m_desc;
			obj["status"] = apply->m_status;
			ret_value["apply_list"].append(obj);
		}
	}

	// 获取好友列表
	std::vector<std::shared_ptr<UserInfo>> friend_list;
	bool b_friend_list = getFriendList(uid, friend_list);
	for (auto& friend_ele : friend_list) {
		Json::Value obj;
		obj["name"] = friend_ele->name;
		obj["uid"] = friend_ele->uid;
		obj["icon"] = friend_ele->icon;
		obj["nick"] = friend_ele->nick;
		obj["sex"] = friend_ele->sex;
		obj["desc"] = friend_ele->desc;
		obj["back"] = friend_ele->back;
		ret_value["friend_list"].append(obj);
	}
	
	auto server_name = ConfigManager::getInstance().getValue("SelfServer", "Name");
	// 将登录数量增加
	auto rd_res = RedisManager::getInstance()->hGet(LOGIN_COUNT, server_name);
	int count = 0;
	if (!rd_res.empty()) {
		count = std::stoi(rd_res);
	}

	count++;
	auto count_str = std::to_string(count);
	// 将登录之后的数量写入redis
	RedisManager::getInstance()->hSet(LOGIN_COUNT, server_name, count_str);
	// session绑定用户uid
	session->setUserId(uid);
	// 为用户设置登录ip server的名字
	std::string ipkey = USERIPPREFIX + uid_str;
	RedisManager::getInstance()->set(ipkey, server_name);
	// uid和session绑定管理,方便以后踢人操作
	UserManager::getInstance()->setUserSession(uid, session);
}

// 搜索好友列表
void LogicSystem::searchInfo(std::shared_ptr<CSession> session, const short& msg_id, const std::string& msg_data)
{
	Json::Reader reader;
	Json::Value root;
	reader.parse(msg_data, root);
	auto uid_str = root["uid"].asString();
	std::cout << "user SearchInfo uid is  " << uid_str << std::endl;

	Json::Value rtvalue;

	Defer defer([this, &rtvalue, session]() {
		std::string return_str = rtvalue.toStyledString();
		session->Send(return_str, ID_SEARCH_USER_RSP);
	});

	bool b_digit = isPureDigit(uid_str);
	if (b_digit) {
		getUserByUid(uid_str, rtvalue);
	}
	else {
		getUserByName(uid_str, rtvalue);
	}
	return;
}

// 添加好友的回复处理
void LogicSystem::addFriendApply(std::shared_ptr<CSession> session, const short& msg_id, const std::string& msg_data)
{
	Json::Reader reader;
	Json::Value root;
	reader.parse(msg_data, root);
	auto uid = root["uid"].asInt();
	auto applyname = root["applyname"].asString();
	auto backname = root["backname"].asString();
	auto touid = root["touid"].asInt();
	std::cout << "user login uid is  " << uid << " applyname  is " << applyname << " bakname is " << backname 
			  << " touid is " << touid << std::endl;

	Json::Value  rtvalue;
	rtvalue["error"] = ErrorCodes::Success;
	Defer defer([this, &rtvalue, session]() {
		std::string return_str = rtvalue.toStyledString();
		session->Send(return_str, ID_ADD_FRIEND_RSP);
	});

	// 先更新Mysql数据库
	MysqlManager::getInstance()->addFriendApply(uid, touid);
	// 查询redis 查找touid对应的server_ip
	auto to_str = std::to_string(touid);
	auto to_ip_key = USERIPPREFIX + to_str;
	std::string to_ip_value = "";
	bool b_ip = RedisManager::getInstance()->get(to_ip_key, to_ip_value);
	if (!b_ip) {
		return;
	}

	auto& cfg = ConfigManager::getInstance();
	auto self_name = cfg["SelfServer"]["Name"];

	std::string base_key = USER_BASE_INFO + std::to_string(uid);
	auto apply_info = std::make_shared<UserInfo>();
	bool b_info = getBaseInfo(base_key, uid, apply_info);

	// 双方在同一个服务器，直接将消息发送给对方
	if (to_ip_value == self_name) {
		auto session = UserManager::getInstance()->getSession(touid);
		if (session) {
			// 在内存中则直接发送通知对方
			Json::Value notify;
			notify["error"] = ErrorCodes::Success;
			notify["applyuid"] = uid;
			notify["name"] = applyname;
			notify["desc"] = "";
			if (b_info) {
				notify["icon"] = apply_info->icon;
				notify["sex"] = apply_info->sex;
				notify["nick"] = apply_info->nick;
			}
			std::string return_str = notify.toStyledString();
			session->Send(return_str, ID_NOTIFY_ADD_FRIEND_REQ);
		}
		return;
	}

	message::AddFriendReq add_req;
	add_req.set_applyuid(uid);
	add_req.set_touid(touid);
	add_req.set_name(applyname);
	add_req.set_desc("");
	if (b_info) {
		add_req.set_icon(apply_info->icon);
		add_req.set_sex(apply_info->sex);
		add_req.set_nick(apply_info->nick);
	}

	// 向对端服务器发送通知
	ChatGrpcClient::getInstance()->notifyAddFriend(to_ip_value, add_req);
}

// 同意好友申请的校验
void LogicSystem::authFriendApply(std::shared_ptr<CSession> session, const short& msg_id, const std::string& msg_data)
{
	Json::Reader reader;
	Json::Value root;
	reader.parse(msg_data, root);

	auto uid = root["fromuid"].asInt();
	auto touid = root["touid"].asInt();
	auto back_name = root["back"].asString();
	std::cout << "from " << uid << " auth friend to " << touid << std::endl;

	Json::Value  rtvalue;
	rtvalue["error"] = ErrorCodes::Success;
	auto user_info = std::make_shared<UserInfo>();

	std::string base_key = USER_BASE_INFO + std::to_string(touid);
	bool b_info = getBaseInfo(base_key, touid, user_info);
	if (b_info) {
		rtvalue["name"] = user_info->name;
		rtvalue["nick"] = user_info->nick;
		rtvalue["icon"] = user_info->icon;
		rtvalue["sex"] = user_info->sex;
		rtvalue["uid"] = touid;
	} else {
		rtvalue["error"] = ErrorCodes::UidInvalid;
	}


	Defer defer([this, &rtvalue, session]() {
		std::string return_str = rtvalue.toStyledString();
		session->Send(return_str, ID_AUTH_FRIEND_RSP);
		});


	// 先更新数据库的好友申请列表，再添加好友
	MysqlManager::getInstance()->authFriendApplyAndAddFriend(uid, touid, back_name);

	//查询redis 查找touid对应的server ip
	auto to_str = std::to_string(touid);
	auto to_ip_key = USERIPPREFIX + to_str;
	std::string to_ip_value = "";
	bool b_ip = RedisManager::getInstance()->get(to_ip_key, to_ip_value);
	if (!b_ip) {
		return;
	}

	auto& cfg = ConfigManager::getInstance();
	auto self_name = cfg["SelfServer"]["Name"];
	// 直接通知对方有认证通过消息
	if (to_ip_value == self_name) {
		auto session = UserManager::getInstance()->getSession(touid);
		if (session) {
			//在内存中则直接发送通知对方
			Json::Value  notify;
			notify["error"] = ErrorCodes::Success;
			notify["fromuid"] = uid;
			notify["touid"] = touid;
			std::string base_key = USER_BASE_INFO + std::to_string(uid);
			auto user_info = std::make_shared<UserInfo>();
			bool b_info = getBaseInfo(base_key, uid, user_info);
			if (b_info) {
				notify["name"] = user_info->name;
				notify["nick"] = user_info->nick;
				notify["icon"] = user_info->icon;
				notify["sex"] = user_info->sex;
			}
			else {
				notify["error"] = ErrorCodes::UidInvalid;
			}


			std::string return_str = notify.toStyledString();
			session->Send(return_str, ID_NOTIFY_AUTH_FRIEND_REQ);
		}

		return;
	}

	message::AuthFriendReq auth_req;
	auth_req.set_fromuid(uid);
	auth_req.set_touid(touid);

	//发送通知
	ChatGrpcClient::getInstance()->notifyAuthFriend(to_ip_value, auth_req);
}

// 转发发送过来的消息
void LogicSystem::dealChatTextMsg(std::shared_ptr<CSession> session, const short& msg_id, const std::string& msg_data)
{
	Json::Reader reader;
	Json::Value root;
	reader.parse(msg_data, root);

	auto uid = root["fromuid"].asInt();
	auto touid = root["touid"].asInt();

	const Json::Value arrays = root["text_array"];

	Json::Value  rtvalue;
	rtvalue["error"] = ErrorCodes::Success;
	rtvalue["text_array"] = arrays;
	rtvalue["fromuid"] = uid;
	rtvalue["touid"] = touid;

	Defer defer([this, &rtvalue, session]() {
		std::string return_str = rtvalue.toStyledString();
		session->Send(return_str, ID_TEXT_CHAT_MSG_RSP);
	});


	// 查询redis 查找touid对应的server ip
	auto to_str = std::to_string(touid);
	auto to_ip_key = USERIPPREFIX + to_str;
	std::string to_ip_value = "";
	bool b_ip = RedisManager::getInstance()->get(to_ip_key, to_ip_value);
	if (!b_ip) {
		return;
	}

	auto& cfg = ConfigManager::getInstance();
	auto self_name = cfg["SelfServer"]["Name"];
	// 在同一个服务器上，则直接通知对方
	if (to_ip_value == self_name) {
		auto session = UserManager::getInstance()->getSession(touid);
		if (session) {
			//在内存中则直接发送通知对方
			std::string return_str = rtvalue.toStyledString();
			session->Send(return_str, ID_NOTIFY_TEXT_CHAT_MSG_REQ);
		}

		return;
	}


	message::TextChatMsgReq text_msg_req;
	text_msg_req.set_fromuid(uid);
	text_msg_req.set_touid(touid);
	for (const auto& txt_obj : arrays) {
		auto content = txt_obj["content"].asString();
		auto msgid = txt_obj["msgid"].asString();
		std::cout << "content is " << content << std::endl;
		std::cout << "msgid is " << msgid << std::endl;
		auto* text_msg = text_msg_req.add_textmsgs();
		text_msg->set_msgid(msgid);
		text_msg->set_msgcontent(content);
	}


	// 发送通知 todo...
	ChatGrpcClient::getInstance()->notifyTextChatMsg(to_ip_value, text_msg_req, rtvalue);
}
